package ai.chat2db.server.web.start.config.config;

import ai.chat2db.server.domain.repository.Dbutils;
import ai.chat2db.server.tools.base.constant.SymbolConstant;
import ai.chat2db.server.tools.base.wrapper.result.ActionResult;
import ai.chat2db.server.tools.common.exception.PermissionDeniedBusinessException;
import ai.chat2db.server.tools.common.exception.RedirectBusinessException;
import ai.chat2db.server.tools.common.model.Context;
import ai.chat2db.server.tools.common.model.LoginUser;
import ai.chat2db.server.tools.common.util.ContextUtils;
import ai.chat2db.server.tools.common.util.I18nUtils;
import ai.chat2db.server.web.start.config.oauth.OauthUtil;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import com.alibaba.fastjson2.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;
import java.util.Enumeration;

/**
 * web project configuration
 *
 * @author Shi Yi
 */
@Configuration
@Slf4j
public class Chat2dbWebMvcConfigurer implements WebMvcConfigurer {

    /**
     * api prefix
     */
    private static final String API_PREFIX = "/api/";

    /**
     * Globally released url(放行)
     */
    private static final String[] FRONT_PERMIT_ALL = new String[]{"/favicon.ico", "/error", "/static/**",
            "/api/system", "/login", "/api/system/get_latest_version"};

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // All requests try to add user information
        registry.addInterceptor(new AsyncHandlerInterceptor() {
                    @Override
                    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
                                             @NotNull Object handler) {
                        Dbutils.setSession();
                        String userIdString = (String) StpUtil.getLoginIdDefaultNull();
                        if (StringUtils.isBlank(userIdString)) {
                            return true;
                        }
                        // 获取用户信息
                        LoginUser loginUser = OauthUtil.getDefaultOauthService().getLoginUser(userIdString);
                        if (loginUser == null) {
                            // Indicates that the user may have been deleted
                            return true;
                        }
                        loginUser.setToken(StpUtil.getTokenValue());
                        ContextUtils.setContext(Context.builder()
                                .loginUser(loginUser)
                                .build());
                        // todo 获取用户权限信息和角色信息
                        return true;
                    }

                    @Override
                    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler,
                                                Exception ex) throws Exception {
                        // Remove login information
                        ContextUtils.removeContext();
                        Dbutils.removeSession();
                    }
                })
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns(FRONT_PERMIT_ALL);

        // Verify login information
        registry.addInterceptor(new AsyncHandlerInterceptor() {
                    @Override
                    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
                                             @NotNull Object handler) throws IOException {
                        Context context = ContextUtils.queryContext();
                        // Verify login information
                        if (context == null) {
                            log.info("Login is required to access {},{}", buildHeaderString(request), SaHolder.getRequest().getUrl());

                            String path = SaHolder.getRequest().getRequestPath();
                            if (path.startsWith(API_PREFIX)) {
                                response.getWriter().println(JSON.toJSONString(
                                        ActionResult.fail("common.needLoggedIn", I18nUtils.getMessage("common.needLoggedIn"),
                                                "")));
                                return false;
                            } else {
                                throw new RedirectBusinessException(
                                        "/login?callback=" + SaFoxUtil.joinParam(request.getRequestURI(),
                                                request.getQueryString()));
                            }
                        }
                        return true;
                    }
                })
                .order(2)
                .addPathPatterns("/**")
                // Links that need to be released on the front end
                .excludePathPatterns(FRONT_PERMIT_ALL)
                // Uniform release ending in -a
                .excludePathPatterns("/**/*-a")
                // Uniform release of endings in _a
                .excludePathPatterns("/**/*_a");

        // Verify permissions
        registry.addInterceptor(new AsyncHandlerInterceptor() {
                    @Override
                    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response,
                                             @NotNull Object handler) throws IOException {
                        LoginUser loginUser = ContextUtils.getLoginUser();
                        if (BooleanUtils.isNotTrue(loginUser.getAdmin())) {
                            throw new PermissionDeniedBusinessException();
                        }
                        return true;
                    }
                })
                .order(3)
                .addPathPatterns("/api/admin/**")
                .addPathPatterns("/admin/**")
        ;

    }

    private String buildHeaderString(HttpServletRequest request) {
        StringBuilder stringBuilder = new StringBuilder();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headName = headerNames.nextElement();
            stringBuilder.append(headName);
            stringBuilder.append(SymbolConstant.COLON);
            stringBuilder.append(request.getHeader(headName));
            stringBuilder.append(SymbolConstant.COMMA);
        }
        return stringBuilder.toString();
    }
}
